Processing Data from the NCAR Mesa Lab Weather Station¶
There is a weather station located at the Mesa Lab, situated along the Foothills of the Rockies in Boulder, Colorado!
By the end of this post, you will be able to plot an interactive visualization of the weather data collected at the Mesa Lab, as shown below!

Here is a picture of the lab and one of the weather stations!


The Data¶
This station collects data every 10 minutes, is publicly available from this site, with live plots viewable here
For this example, we downloaded a weeks’s worth of daily data from this past week. You can access the FTP server using this this link, pulling data from the /mesa directory.
Imports¶
In this example, we utilize xarray and pandas for data cleaning, and hvplot/holoviews for visualization!
import holoviews as hv
import hvplot
import hvplot.xarray
import pandas as pd
import xarray as xr
from metpy.units import units
hv.extension('bokeh')
Reading in the Data¶
We can wrap this into a function, then pass this into xr.open_mfdataset to process multiple files at the same time!
ds = xr.open_mfdataset('data/mlab*', engine='netcdf4', concat_dim='time').load()
ds
<xarray.Dataset>
Dimensions: (time: 4043)
Coordinates:
* time (time) datetime64[ns] 2021-11-01 ... 2021-11-15
Data variables: (12/17)
lat (time) float64 39.98 39.98 39.98 39.98 ... 39.98 39.98 39.98
lon (time) float64 -105.3 -105.3 -105.3 ... -105.3 -105.3 -105.3
alt (time) float64 1.885e+03 1.885e+03 ... 1.885e+03 1.885e+03
pres (time) float64 813.3 813.4 813.4 813.5 ... 811.0 810.9 810.8
rain_accum (time) float64 408.9 408.9 408.9 408.9 ... 410.8 410.8 410.8
rh (time) float64 75.6 75.3 75.3 75.5 75.6 ... 26.2 26.5 26.6 26.5
... ...
V (time) float64 -2.252 -1.386 -2.035 ... -5.129 -2.666 -4.623
dewpoint (time) float64 -2.139 -2.096 -2.193 ... -3.314 -3.349 -3.314
cpres0 (time) float64 1.022e+03 1.022e+03 ... 1.019e+03 1.019e+03
windchill (time) float64 -0.5018 1.8 0.1822 -1.764 ... 6.599 7.137 6.091
raina_event (time) float64 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0
raina_daily (time) float64 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0
Attributes:
site_name: NCAR Mesa Lab
site_location: NCAR Mesa Lab, South West Boulder, CO
Conventions: CF-1.7- time: 4043
- time(time)datetime64[ns]2021-11-01 ... 2021-11-15
- long_name :
- time
array(['2021-11-01T00:00:00.000000000', '2021-11-01T00:05:00.000000000', '2021-11-01T00:10:00.000000000', ..., '2021-11-14T23:50:00.000000000', '2021-11-14T23:55:00.000000000', '2021-11-15T00:00:00.000000000'], dtype='datetime64[ns]')
- lat(time)float6439.98 39.98 39.98 ... 39.98 39.98
- units :
- degree
- long_name :
- North latitude
array([39.98, 39.98, 39.98, ..., 39.98, 39.98, 39.98])
- lon(time)float64-105.3 -105.3 ... -105.3 -105.3
- units :
- degree
- long_name :
- East longitude
array([-105.275, -105.275, -105.275, ..., -105.275, -105.275, -105.275])
- alt(time)float641.885e+03 1.885e+03 ... 1.885e+03
- units :
- meters
- long_name :
- Altitude above MSL
array([1885., 1885., 1885., ..., 1885., 1885., 1885.])
- pres(time)float64813.3 813.4 813.4 ... 810.9 810.8
- units :
- hPa
- long_name :
- Air pressure
array([813.299988, 813.400024, 813.400024, ..., 811. , 810.900024, 810.799988]) - rain_accum(time)float64408.9 408.9 408.9 ... 410.8 410.8
- units :
- mm
- long_name :
- Rain accumulation
array([408.899994, 408.899994, 408.899994, ..., 410.799988, 410.799988, 410.799988]) - rh(time)float6475.6 75.3 75.3 ... 26.5 26.6 26.5
- units :
- %
- long_name :
- Relative humidity
array([75.599998, 75.300003, 75.300003, ..., 26.5 , 26.6 , 26.5 ]) - tdry(time)float641.7 1.8 1.7 1.7 ... 15.9 15.8 15.9
- units :
- degC
- long_name :
- Air temperature
array([ 1.7, 1.8, 1.7, ..., 15.9, 15.8, 15.9])
- wdir(time)float64330.0 330.0 328.0 ... 285.0 291.0
- units :
- degrees
- long_name :
- Wind direction average
array([330., 330., 328., ..., 296., 285., 291.])
- wspd(time)float642.6 1.6 2.4 3.0 ... 11.7 10.3 12.9
- units :
- m/s
- long_name :
- Wind speed average
array([ 2.6, 1.6, 2.4, ..., 11.7, 10.3, 12.9])
- wspd_max(time)float644.0 3.0 4.2 4.1 ... 21.0 20.2 24.3
- units :
- m/s
- long_name :
- Wind speed maximum
array([ 4. , 3. , 4.2 , ..., 21. , 20.200001, 24.299999]) - U(time)float641.3 0.8 1.272 ... 10.52 9.949 12.04
- units :
- m/s
- long_name :
- Northward wind component
array([ 1.3 , 0.8 , 1.27180623, ..., 10.51589034, 9.94903601, 12.0431875 ]) - V(time)float64-2.252 -1.386 ... -2.666 -4.623
- units :
- m/s
- long_name :
- Eastward wind component
array([-2.25166605, -1.38564065, -2.03531543, ..., -5.12894242, -2.66583616, -4.62294655]) - dewpoint(time)float64-2.139 -2.096 ... -3.349 -3.314
- units :
- degC
- long_name :
- Dewpoint
array([-2.13926962, -2.09630533, -2.19291235, ..., -3.31382551, -3.34891419, -3.31382551]) - cpres0(time)float641.022e+03 1.022e+03 ... 1.019e+03
- units :
- hPa
- long_name :
- Pressure corrected to sea level
array([1021.68262564, 1021.80295459, 1021.80295459, ..., 1018.91592975, 1018.79566074, 1018.67531905]) - windchill(time)float64-0.5018 1.8 0.1822 ... 7.137 6.091
- units :
- degC
- long_name :
- Wind chill
array([-0.50178996, 1.8 , 0.18221914, ..., 6.59902266, 7.13700635, 6.09111839]) - raina_event(time)float640.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
- units :
- mm
- long_name :
- Rain accumulation reset 1 hour after event
array([0., 0., 0., ..., 0., 0., 0.])
- raina_daily(time)float640.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
- units :
- mm
- long_name :
- Rain accumulation reset at local midnight
array([0., 0., 0., ..., 0., 0., 0.])
- site_name :
- NCAR Mesa Lab
- site_location :
- NCAR Mesa Lab, South West Boulder, CO
- Conventions :
- CF-1.7
We can plot a basic plot using the .plot() method in xarray
ds.tdry.plot();
Plotting an Interactive Meteogram¶
We can also plot a “meteogram” which is a collection of different surface observations.
We know from the .nc files that variables have the following units:
Temperature (
tdry) -degrees CelsiusDewpoint (
dewpoint) -degrees CelsiusWind (
wspd)-meter/secondMax Wind Speed (
wspd_max) -meter/second
We start by defining our plots, as shown below:
Converting to Standard Units¶
We can use metpy.units here to convert to standard units!
ds_standard_units = ds.copy()
# Convert windspeed
ds_standard_units['wspd'] = ('time', (ds.wspd.values * units('m/s')).to('mph'))
ds_standard_units.wspd.attrs['units'] = 'mph'
# Convert max windspeed
ds_standard_units['wspd_max'] = ('time', (ds.wspd_max.values * units('m/s')).to('mph'))
ds_standard_units.wspd_max.attrs['units'] = 'mph'
# Convert windspeed
ds_standard_units['tdry'] = ('time', (ds.tdry.values * units('degC')).to('degF'))
ds_standard_units.tdry.attrs['units'] = 'degF'
# Convert max windspeed
ds_standard_units['dewpoint'] = ('time', (ds.dewpoint.values * units('degC')).to('degF'))
ds_standard_units.dewpoint.attrs['units'] = 'degF'
Setup our Wind Plots¶
We now can setup our plots - starting with wind speed. We add a few labels, and merge them within the same subplot using the * syntax in holoviews!
wind_speed_plot = ds_standard_units.wspd.hvplot.line(
ylabel='Wind Speed (mph)', color='darkblue', label='Wind Speed'
)
wind_speed_max_plot = ds_standard_units.wspd_max.hvplot.scatter(
color='lightblue', label='Peak Wind Speed'
)
wind_speed_max_plot * wind_speed_plot
Setup our Temperature Plots¶
We follow the same process for our temperature/dewpoint plots, adding an alpha argument to lighten the colors a bit.
temperature_plot = ds_standard_units.tdry.hvplot.line(
ylabel='Temperature (degF)', color='red', label='Temperature', alpha=0.4
)
dewpoint_plot = ds_standard_units.dewpoint.hvplot.line(color='green', label='Dewpoint', alpha=0.4)
temperature_plot * dewpoint_plot
Bringing it All Together¶
Now that our plots are all setup, we can merge them into the same figure using the following syntax, specifying a single column and a legend located in the top left of each subplot:
hv.Layout(
(wind_speed_max_plot * wind_speed_plot).opts(legend_position='top_left')
+ (temperature_plot * dewpoint_plot).opts(legend_position='top_left')
).cols(1)